Geospatial analysis in GeoPandas¶

Further learning: https://autogis-site.readthedocs.io/en/latest/index.html

Norway shapefile: https://kartkatalog.geonorge.no/metadata/administrative-enheter-fylker/6093c8a8-fa80-11e6-bc64-92361f002671

GeoPandas, as the name suggests, extends the popular data science library pandas by adding support for geospatial data. If you are not familiar with pandas, we recommend taking a quick look at its Getting started documentation before proceeding.

The core data structure in GeoPandas is the geopandas.GeoDataFrame, a subclass of pandas.DataFrame, that can store geometry columns and perform spatial operations. The geopandas.GeoSeries, a subclass of pandas.Series, handles the geometries. Therefore, your GeoDataFrame is a combination of pandas.Series, with traditional data (numerical, boolean, text etc.), and geopandas.GeoSeries, with geometries (points, polygons etc.). You can have as many columns with geometries as you wish; there’s no limit typical for desktop GIS software.

Each GeoSeries can contain any geometry type (you can even mix them within a single array) and has a GeoSeries.crs attribute, which stores information about the projection (CRS stands for Coordinate Reference System). Therefore, each GeoSeries in a GeoDataFrame can be in a different projection, allowing you to have, for example, multiple versions (different projections) of the same geometry.

Only one GeoSeries in a GeoDataFrame is considered the active geometry, which means that all geometric operations applied to a GeoDataFrame operate on this active column.

In [5]:
import geopandas as gpd

Reading files¶

Assuming you have a file containing both data and geometry (e.g. GeoPackage, GeoJSON, Shapefile), you can read it using geopandas.read_file(), which automatically detects the filetype and creates a GeoDataFrame. This tutorial uses the "nybb" dataset, a map of New York boroughs, which is part of the GeoPandas installation. Therefore, we use geopandas.datasets.get_path() to retrieve the path to the dataset.

In [124]:
gdf = gpd.read_file('data/trafikkregistreringsstasjoner_e6.geojson')
In [125]:
gdf
Out[125]:
Registreringsnivå Antall kjørefelt Status Stasjonsnavn Målestasjonsnummer id metadata vegsystem strekning kortform veglenkesekvensid relativPosisjon veglenkeType detaljnivå startdato kommune fylke sluttdato geometry
0 Kontinuerlig (Nivå 1) 2.0 Operativ BIRI SØR 500513 370075065 {'type': {'id': 482, 'navn': 'Trafikkregistrer... {'id': 1002317285, 'versjon': 1, 'vegkategori'... {'id': -1, 'versjon': -1, 'strekning': 34, 'de... EV6 S34D1 m1024 1060614 0.161844 HOVED Vegtrase 2021-11-08 3407 34 None POINT Z (264149.468 6763845.476 126.247)
1 Kontinuerlig (Nivå 1) 6.0 Operativ SKULLERUD 300039 370075067 {'type': {'id': 482, 'navn': 'Trafikkregistrer... {'id': 1006190638, 'versjon': 1, 'vegkategori'... {'id': -1, 'versjon': -1, 'strekning': 15, 'de... EV6 S15D1 m5271 625212 0.546885 HOVED Vegtrase 2021-11-29 301 3 None POINT Z (266588.338 6643481.572 96.660)
2 Kontinuerlig (Nivå 1) 4.0 Operativ LEIRELVA BRU 200211 370075069 {'type': {'id': 482, 'navn': 'Trafikkregistrer... {'id': 1002316638, 'versjon': 1, 'vegkategori'... {'id': -1, 'versjon': -1, 'strekning': 20, 'de... EV6 S20D1 m5058 444220 0.305518 HOVED Vegtrase 2021-11-29 3030 30 None POINT Z (282658.009 6661281.184 122.767)
3 Kontinuerlig (Nivå 1) 6.0 Midlertidig ute av drift E6 V/KARIHAUGEN 300349 370075137 {'type': {'id': 482, 'navn': 'Trafikkregistrer... {'id': 1002316825, 'versjon': 1, 'vegkategori'... {'id': -1, 'versjon': -1, 'strekning': 17, 'de... EV6 S17D1 m5998 625215 0.798544 HOVED Vegtrase 2021-11-29 301 3 None POINT Z (272890.578 6651710.794 177.096)
4 Kontinuerlig (Nivå 1) 2.0 Operativ Nordkjosbotn sør 1900211 370075139 {'type': {'id': 482, 'navn': 'Trafikkregistrer... {'id': 1006190674, 'versjon': 1, 'vegkategori'... {'id': -1, 'versjon': -1, 'strekning': 179, 'd... EV6 S179D1 m12163 1126289 0.567433 HOVED Vegtrase og kjørebane 2021-12-01 5422 54 None POINT Z (679838.689 7684871.107 3.039)
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
242 Kontinuerlig (Nivå 1) NaN Operativ LANGFJORDBOTN X882 2000009 1014031313 {'type': {'id': 482, 'navn': 'Trafikkregistrer... {'id': 1006190681, 'versjon': 1, 'vegkategori'... {'id': -1, 'versjon': -1, 'strekning': 199, 'd... EV6 S199D1 m1854 930225 0.217697 HOVED Vegtrase og kjørebane 2021-12-01 5403 54 None POINT Z (775891.977 7786772.901 39.026)
243 Kontinuerlig (Nivå 1) NaN Operativ Bjørkheim X 3000367 1014047386 {'type': {'id': 482, 'navn': 'Trafikkregistrer... {'id': 1006190636, 'versjon': 1, 'vegkategori'... {'id': -1, 'versjon': -1, 'strekning': 243, 'd... EV6 S243D1 m7212 2604164 0.225644 HOVED Vegtrase og kjørebane 2021-09-28 5444 54 None POINT Z (1076207.450 7802256.597 60.649)
244 Kontinuerlig (Nivå 1) NaN Operativ E6 Alnabru Nordgående Morpunkt 300146 1014062385 {'type': {'id': 482, 'navn': 'Trafikkregistrer... {'id': 1006190639, 'versjon': 1, 'vegkategori'... {'id': -1, 'versjon': -1, 'strekning': 16, 'de... EV6 S16D1 m6497 625213 0.896827 HOVED Vegtrase og kjørebane 2021-11-29 301 3 None POINT Z (266860.793 6650152.076 95.680)
245 Kontinuerlig (Nivå 1) NaN Operativ Grong 1700019 1014067467 {'type': {'id': 482, 'navn': 'Trafikkregistrer... {'id': 1002317857, 'versjon': 1, 'vegkategori'... {'id': -1, 'versjon': -1, 'strekning': 97, 'de... EV6 S97D1 m9121 578609 0.909206 HOVED Vegtrase 2021-11-23 5045 50 None POINT Z (370173.334 7151626.029 25.234)
246 Kontinuerlig (Nivå 1) NaN Operativ E6 SYNSTERUD 3000501 1014096768 {'type': {'id': 482, 'navn': 'Trafikkregistrer... {'id': 1002317896, 'versjon': 1, 'vegkategori'... {'id': -1, 'versjon': -1, 'strekning': 29, 'de... EV6 S29D1 m10832 2823418 0.031060 HOVED Vegtrase 2021-10-14 3413 34 None POINT Z (295209.700 6736968.012 194.489)

247 rows × 19 columns

Writing files¶

To write a GeoDataFrame back to file use GeoDataFrame.to_file(). The default file format is Shapefile, but you can specify your own with the driver keyword.

In [ ]:
gdf.to_file("my_file.geojson", driver="GeoJSON")

Built-in functions¶

In [67]:
norge = gpd.read_file('./data/norge.geojson')
In [68]:
norge
Out[68]:
objtype samiskforvaltningsomrade lokalid navnerom versjonid datafangstdato oppdateringsdato datauttaksdato opphav fylkesnummer navn geometry
0 Fylke false 3a65f9e4-23d5-4c05-b188-8f4ea8463f8b http://skjema.geonorge.no/SOSI/produktspesifik... 4.1 20190301000000 20211209122812 None 42 [ { "navn": "Agder", "objtype": "Administrativ... POLYGON ((12650.920 6523939.770, 12654.850 652...
1 Fylke false 6fa6cac4-9f86-4cbf-9ae4-716eb5059541 http://skjema.geonorge.no/SOSI/produktspesifik... 4.1 20200207000000 20211209122812 None 03 [ { "navn": "Oslo", "objtype": "AdministrativE... POLYGON ((255493.650 6656227.710, 255593.360 6...
2 Fylke JA 545cbfe3-2f34-4792-a9e9-135375accefd http://skjema.geonorge.no/SOSI/produktspesifik... 4.1 20200207000000 20211209122812 None 54 [ { "navn": "Troms og Finnmark", "objtype": "A... POLYGON ((701859.740 7853460.760, 680484.030 7...
3 Fylke false ab9f3402-23fb-48b4-a4fd-dbe4e3cc321b http://skjema.geonorge.no/SOSI/produktspesifik... 4.1 20200207000000 20211209122812 None 15 [ { "navn": "Møre og Romsdal", "objtype": "Adm... POLYGON ((-20575.890 6963321.310, -25091.800 6...
4 Fylke false 22642144-2de8-4e9d-bea0-0520fb44e85a http://skjema.geonorge.no/SOSI/produktspesifik... 4.1 20190913000000 20211209122812 None 38 [ { "navn": "Vestfold og Telemark", "objtype":... POLYGON ((78548.430 6606728.760, 78556.900 660...
5 Fylke JA ea3b82cf-f063-41a9-a19c-449832aadf3a http://skjema.geonorge.no/SOSI/produktspesifik... 4.1 20200207000000 20211209122812 None 50 [ { "navn": "Trøndelag", "objtype": "Administr... POLYGON ((228157.810 7170172.040, 207751.950 7...
6 Fylke false e70c69ba-5bea-48a8-952c-b4860ada5afa http://skjema.geonorge.no/SOSI/produktspesifik... 4.1 20200207000000 20211209122812 None 11 [ { "navn": "Rogaland", "objtype": "Administra... POLYGON ((-92079.540 6636303.790, -96094.590 6...
7 Fylke false f96e22cd-cd45-48a3-99b8-a3d5b78c76b4 http://skjema.geonorge.no/SOSI/produktspesifik... 4.1 20190225000000 20211209122812 None 34 [ { "navn": "Innlandet", "objtype": "Administr... POLYGON ((125061.480 6809053.300, 125036.470 6...
8 Fylke false f2e85556-471b-4807-a1bf-6da7478e7cda http://skjema.geonorge.no/SOSI/produktspesifik... 4.1 20190225000000 20211209122812 None 30 [ { "navn": "Viken", "objtype": "Administrativ... POLYGON ((162857.290 6678845.090, 163199.630 6...
9 Fylke JA ee7fa255-8172-47f2-8f83-6076ccf1bb4f http://skjema.geonorge.no/SOSI/produktspesifik... 4.1 20211115000000 20211209122812 None 18 [ { "navn": "Nordland", "objtype": "Administra... POLYGON ((372929.380 7521632.830, 360585.200 7...
10 Fylke false 173150173151 http://skjema.geonorge.no/SOSI/produktspesifik... 4.1 20200207000000 20211209122813 None 46 [ { "navn": "Vestland", "objtype": "Administra... POLYGON ((-86165.900 6792746.680, -84865.720 6...

Projections¶

Each GeoSeries has its Coordinate Reference System (CRS) accessible at GeoSeries.crs. The CRS tells GeoPandas where the coordinates of the geometries are located on the earth’s surface. In some cases, the CRS is geographic, which means that the coordinates are in latitude and longitude. In those cases, its CRS is WGS84, with the authority code EPSG:4326. Let’s see the projection of our Norway GeoDataFrame.

In [90]:
norge.crs
Out[90]:
<Derived Projected CRS: EPSG:25833>
Name: ETRS89 / UTM zone 33N
Axis Info [cartesian]:
- E[east]: Easting (metre)
- N[north]: Northing (metre)
Area of Use:
- name: Europe between 12°E and 18°E: Austria; Denmark - offshore and offshore; Germany - onshore and offshore; Norway including Svalbard - onshore and offshore.
- bounds: (12.0, 46.4, 18.0, 84.42)
Coordinate Operation:
- name: UTM zone 33N
- method: Transverse Mercator
Datum: European Terrestrial Reference System 1989 ensemble
- Ellipsoid: GRS 1980
- Prime Meridian: Greenwich
In [70]:
norge.area
Out[70]:
0     2.305731e+10
1     4.810366e+08
2     1.161332e+11
3     2.554877e+10
4     2.024325e+10
5     5.968706e+10
6     1.663051e+10
7     5.211183e+10
8     2.584184e+10
9     8.050724e+10
10    4.986524e+10
dtype: float64
In [71]:
norge.boundary
Out[71]:
0     LINESTRING (12650.920 6523939.770, 12654.850 6...
1     LINESTRING (255493.650 6656227.710, 255593.360...
2     LINESTRING (701859.740 7853460.760, 680484.030...
3     LINESTRING (-20575.890 6963321.310, -25091.800...
4     LINESTRING (78548.430 6606728.760, 78556.900 6...
5     LINESTRING (228157.810 7170172.040, 207751.950...
6     LINESTRING (-92079.540 6636303.790, -96094.590...
7     LINESTRING (125061.480 6809053.300, 125036.470...
8     MULTILINESTRING ((162857.290 6678845.090, 1631...
9     LINESTRING (372929.380 7521632.830, 360585.200...
10    LINESTRING (-86165.900 6792746.680, -84865.720...
dtype: geometry
In [72]:
norge.centroid
Out[72]:
0      POINT (76085.466 6511179.375)
1     POINT (262250.904 6656330.653)
2     POINT (821585.665 7786813.996)
3     POINT (101811.400 6977403.140)
4     POINT (154922.651 6602359.966)
5     POINT (298890.055 7093647.954)
6     POINT (-23047.568 6579824.752)
7     POINT (256976.692 6820201.705)
8     POINT (220137.581 6666944.356)
9     POINT (452632.751 7445560.745)
10     POINT (11295.203 6786847.403)
dtype: geometry

Measure distance between points:

In [73]:
point = norge.centroid.iloc[0]
norge.distance = norge.centroid.distance(point)
norge.distance
Out[73]:
0     0.000000e+00
1     2.360645e+05
2     1.477503e+06
3     4.669330e+05
4     1.205371e+05
5     6.236277e+05
6     1.205800e+05
7     3.580732e+05
8     2.121644e+05
9     1.007401e+06
10    2.831795e+05
dtype: float64

Note that geopandas.GeoDataFrame is a subclass of pandas.DataFrame, so we have all the pandas functionality available to use on the geospatial dataset — we can even perform data manipulations with the attributes and geometry information together.

For example, to calculate the average of the distances measured above, access the distance column and call the mean() method on it:

In [74]:
norge.distance.mean()
Out[74]:
446005.7367762395

Making maps¶

GeoPandas can also plot maps, so we can check how the geometries appear in space. To plot the active geometry, call GeoDataFrame.plot(). To color code by another column, pass in that column as the first argument. In the example below, we plot the active geometry column and color code by the "area" column. We also want to show a legend (legend=True).

In [77]:
norge.plot('fylkesnummer', legend=True, figsize=(10,10))
Out[77]:
<AxesSubplot:>
In [79]:
norge.explore('fylkesnummer', legend=True)
Out[79]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Let's plot E6 traffic registration stations on the map of Norway

In [126]:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(15,9))
ax.set_aspect('equal')
norge.plot('fylkesnummer', ax=ax)
gdf.plot(ax=ax, color='black')
Out[126]:
<AxesSubplot:>

Geojson data from Vegkart¶

A lot of interesting open data is available from Statens Vegvesen via Vegkart: https://vegkart.atlas.vegvesen.no/

All available data is listed in the Datakatalog: https://datakatalogen.vegdata.no/

We now learn how to import this data into Geopandas. There is a Python library available to extract data from Vegkart: https://github.com/LtGlahn/nvdbapi-V3. However, version 3 of this package does not include the functionality to convert the data to GeoJSON format. I've extracted that code from version 2 of nvdbapi and imported it into my own fork of version 3: https://github.com/alexdiem/nvdbapi-V3. We will use this code to import Vegkart data into GeoPandas.

In [152]:
import nvdbapiv3 as nvdb
from nvdbapiv3 import nvdb2geojson

The data saved in Vegkart is encoded into numerical IDs according to Datakatalog:

In [167]:
q = nvdb.nvdbFagdata(482) # Trafikkregistreringsstasjoner

# (Status=Operativ OR Status=Midlertidig ute av drift) AND Trafikantgruppe=Sykkel
q.filter({"egenskap": "(5201=7081 OR 5201=12987) AND 9293=12993"})
In [166]:
q_geo = nvdb2geojson.fagdata2geojson(q)
qdf = gpd.GeoDataFrame.from_features(q_geo['features'])
qdf.set_crs(epsg=25833)
Out[166]:
geometry Geometri, punkt Regulering gangfelt (Ny) Regulering vegkryss (Ny) UID (Ny) Ukedagstype (Ny) Ulykkestype (Ny) Ulykkestype underkategori (Ny) År (Ny) Antall Andre enheter (Ny) ... kommune fylke Temperatur Historisk Kryssdel (Ny) Historisk Kryssdel meterverdi (Ny) kryssystem sluttdato Historisk Sideanleggsdel (Ny) Historisk Sideanleggsdel meterverdi (Ny) sideanlegg
0 POINT Z (544533.382 7568315.821 4.924) POINT (544528 7568318) Ikke gangfelt Ukjent UID0257461683 Helgedøgn Fotgjenger/akende Fotgjenger krysset kjørebanen 2020 0 ... 1875 18 NaN NaN NaN NaN NaN NaN NaN NaN
1 POINT Z (1014115.062 7843588.599 4.654) POINT (1014110 7843577) Ikke gangfelt Ikke i vegkryss UID1287984162 Yrkesdøgn Utforkjøring Enslig kjøretøy kjørte utfor vegen 2020 0 ... 5442 54 6.0 NaN NaN NaN NaN NaN NaN NaN
2 POINT Z (540007.924 7498047.595 13.952) POINT (540015 7498073) Ikke gangfelt Ikke i vegkryss UID8346400236 Yrkesdøgn Motsatt kjøreretning Ulykke ved møting 2020 0 ... 1845 18 17.0 NaN NaN NaN NaN NaN NaN NaN
3 POINT Z (95855.987 6930249.666 14.410) POINT (95856 6930251) Ikke gangfelt Ikke i vegkryss UID1936715838 Helgedøgn Motsatt kjøreretning Ulykke ved møting 2020 0 ... 1578 15 17.0 NaN NaN NaN NaN NaN NaN NaN
4 POINT Z (-33337.425 6569927.324 49.668) POINT (-33324 6569920) Ikke gangfelt Ukjent UID2557581828 Yrkesdøgn Samme kjøreretning Ulykke mellom kjøretøy med samme kjøreretning 2020 0 ... 1103 11 NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
3536 POINT Z (274731.099 7040232.000 103.167) POINT (274732 7040227) Gangfelt i kryss Uregulert kryss, generell høyreregel (uten ski... UID6677519512 Helgedøgn Fotgjenger/akende Fotgjenger krysset kjørebanen 2020 0 ... 5001 50 NaN NaN NaN NaN NaN NaN NaN NaN
3537 POINT Z (261790.947 7027311.614 128.693) POINT (261788 7027314) Ikke gangfelt Ikke i vegkryss UID0058200042 Helgedøgn Utforkjøring Enslig kjøretøy kjørte utfor vegen 2020 0 ... 5028 50 10.0 NaN NaN NaN NaN NaN NaN NaN
3538 POINT Z (259233.075 6614798.935 81.879) POINT (259236 6614800) Ikke gangfelt Vikepliktsregulert kryss (skilt 202, 204, 210) UID6849910007 Yrkesdøgn Kryssende kjørereretning Ulykke ved kryssende kjøreretning hvor kjøretø... 2020 0 ... 3019 30 13.0 NaN NaN NaN NaN NaN NaN NaN
3539 POINT Z (537183.615 7506739.507 140.916) POINT (537184 7506737) Ikke gangfelt Ukjent UID2579406278 Yrkesdøgn Samme kjøreretning Ulykke mellom kjøretøy med samme kjøreretning 2020 0 ... 1845 18 6.0 NaN NaN NaN NaN NaN NaN NaN
3540 POINT Z (365817.819 7239876.120 4.487) POINT (365820 7239877) Ikke gangfelt Ikke i vegkryss UID2323791110 Yrkesdøgn Utforkjøring Enslig kjøretøy kjørte utfor vegen 2020 0 ... 1812 18 NaN NaN NaN NaN NaN NaN NaN NaN

3541 rows × 84 columns